﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Anagramme.Model;
using System.Windows.Input;
using System.ApplicationModel;
using System.ComponentModel.Composition.Hosting;
using Anagramme.Views;
using System.ComponentModel.Composition;
using System.ComponentModel.DataAnnotations;
using System.ApplicationModel.Composition;
using Anagramme.Contracts;
using System.Windows;
using Anagramme.Services;
using System.Activities;
using Anagramme.Activities.BusinessRules;

namespace Anagramme.ViewModels
{
    /// <summary>
    /// ViewModel de l'accueil
    /// </summary>
    [ViewModelExport(Modules.Home)]
    public class HomeViewModel : ViewModelBase<ILobbyModel>
    {
        #region Constructors
        /// <summary>
        /// Crée une nouvelle instance de la classe <see cref="HomeViewModel"/>
        /// </summary>
        public HomeViewModel()
        {
            this.PlayerName = Properties.Settings.Default.PlayerName;
        }
        #endregion

        #region Fields
        private string _playerName;

        private ICommand _createGameCommand;
        private ICommand _exitCommand;
        private ICommand _joinGameCommand;
        private ICommand _saveOptionsCommand;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient ou définit le nom du joueur
        /// </summary>
        [Required(AllowEmptyStrings=false, ErrorMessageResourceName="PLAYER_NAME_REQUIRED", ErrorMessageResourceType=typeof(Resources.HomeStrings))]
        public string PlayerName
        {
            get { return this._playerName; }
            set
            {
                if (this._playerName != value)
                {
                    base.RaisePropertyChanging();
                    this._playerName = value;
                    base.TraceAssign(value);
                    base.RaisePropertyChanged();
                }
            }
        }
        #endregion

        #region Commands
        /// <summary>
        /// Obtient la commande pour créer une nouvelle partie
        /// </summary>
        public ICommand CreateGameCommand
        {
            get
            {
                if (object.ReferenceEquals(this._createGameCommand, null))
                {
                    this._createGameCommand = new DelegateCommand(this.CreateGame, this.CanCreateGame);
                }
                return this._createGameCommand;
            }
        }

        /// <summary>
        /// Obtient la commande pour quitter l'application
        /// </summary>
        public ICommand ExitCommand
        {
            get
            {
                if (object.ReferenceEquals(this._exitCommand, null))
                {
                    this._exitCommand = new DelegateCommand(this.Exit, this.CanExit);
                }
                return this._exitCommand;
            }
        }

        /// <summary>
        /// Obtient la commande pour rejoindre une partie existante
        /// </summary>
        public ICommand JoinGameCommand
        {
            get
            {
                if (object.ReferenceEquals(this._joinGameCommand, null))
                {
                    this._joinGameCommand = new DelegateCommand(this.JoinGame, this.CanJoinGame);
                }
                return this._joinGameCommand;
            }
        }

        /// <summary>
        /// Obtient la commande pour sauvegarder les options
        /// </summary>
        public ICommand SaveOptionsCommand
        {
            get
            {
                if (object.ReferenceEquals(this._saveOptionsCommand, null))
                {
                    this._saveOptionsCommand = new DelegateCommand(this.SaveOptions, this.CanSaveOptions);
                }
                return this._saveOptionsCommand;
            }
        }
        #endregion

        #region Model callback
        private void Model_OnResponseLobbyJoin(object sender, EventArgs<Guid> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<ILobbyModel, EventArgs<Guid>>.RemoveHandler(this.Model, "OnResponseLobbyJoin", this.Model_OnResponseLobbyJoin);

            Guid token = e.Value;
            ApplicationServices.Trace.TraceData("token", token);

            if (!token.Equals(Guid.Empty))
            {
                IResultsModel resultsModel = ApplicationServices.Container.GetExportedValue<IResultsModel>();
                IGameModel gameModel = ApplicationServices.Container.GetExportedValue<IGameModel>();

                resultsModel.Authenticate(token);
                gameModel.Authenticate(token);

                base.BeginWaitingAction();
                WeakEventManager<ILobbyModel, EventArgs<Player>>.AddHandler(base.Model, "OnResponseLobbyPlayerInfo", this.Model_OnResponseLobbyPlayerInfo);
                this.Model.RequestLobbyPlayerInfo();
            }
            else
            {
                ApplicationServices.Message.ShowMessage(Resources.HomeStrings.SERVER_FULL);
            }
            base.EndWaitingAction();

            ApplicationServices.Trace.TraceMethodEntering();
        }

        private void Model_OnResponseLobbyPlayerInfo(object sender, EventArgs<Player> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<ILobbyModel, EventArgs<Player>>.RemoveHandler(base.Model, "OnResponseLobbyPlayerInfo", this.Model_OnResponseLobbyPlayerInfo);
            base.Player = e.Value;
            base.EndWaitingAction();

            ApplicationServices.ViewWorkflow.SendMessage(Messages.HomeCreateGame);

            ApplicationServices.Trace.TraceMethodEntering();
        }
        #endregion

        #region Methods
        private bool CanCreateGame()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy },
                { "PlayerName", Properties.Settings.Default.PlayerName }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyWithPlayerNameActivity(), arguments)["ReturnValue"];
            
            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        [System.Security.SecurityCritical]
        private void CreateGame()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            base.BeginWaitingAction();

            var fileName = System.IO.Path.Combine(Environment.CurrentDirectory, "server.exe");
            System.Diagnostics.Process.Start(fileName, "-s");

            List<IService> services = ApplicationServices.Container.GetExportedValues<IService>().ToList();

            services.ForEach(s => s.Connect());

            WeakEventManager<ILobbyModel, EventArgs<Guid>>.AddHandler(this.Model, "OnResponseLobbyJoin", this.Model_OnResponseLobbyJoin);
            base.Model.RequestLobbyJoin(Properties.Settings.Default.PlayerName);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private bool CanExit()
        {
            ApplicationServices.Trace.TraceMethodEntering();
            
            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyActivity(), arguments)["ReturnValue"];

            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void Exit()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            System.ApplicationModel.Application.Current.Shutdown();
        }

        private bool CanJoinGame()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy },
                { "PlayerName", Properties.Settings.Default.PlayerName }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyWithPlayerNameActivity(), arguments)["ReturnValue"];

            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void JoinGame()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            ApplicationServices.ViewWorkflow.SendMessage(Messages.HomeJoinGame);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private bool CanSaveOptions()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy },
                { "PlayerName", Properties.Settings.Default.PlayerName }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyWithPlayerNameActivity(), arguments)["ReturnValue"];

            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void SaveOptions()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Properties.Settings.Default.PlayerName = this.PlayerName;
            Properties.Settings.Default.Save();

            ApplicationServices.Trace.TraceMethodLeaving();
        }
        #endregion
    }
}
